#include "rsa.h"


OPEN_JLIB_NS


bool RSA::setup_private (const c8 *filename) noexcept {
  if (this->rsa_pri)
    RSA_free(this->rsa_pri);
  BIO *bio = BIO_new_file(filename, "r");
  this->rsa_pri = PEM_read_bio_RSAPrivateKey(bio, nullptr, nullptr, nullptr);
  BIO_free(bio);
  if (this->rsa_pri) {
    this->blk_pri = (u64)RSA_size(this->rsa_pri);
    this->max_pri = this->blk_pri - RSA_PKCS1_PADDING_SIZE;
    return true;
  }
  else {
    this->blk_pri = 0;
    this->max_pri = 0;
    return false;
  }
}



bool RSA::setup_private (const void *key, uwl size) noexcept {
  if (this->rsa_pri)
    RSA_free(this->rsa_pri);
  BIO *bio = BIO_new_mem_buf((void*)key, (s32)size);
  this->rsa_pri = PEM_read_bio_RSAPrivateKey(bio, nullptr, nullptr, nullptr);
  BIO_free(bio);
  if (this->rsa_pri) {
    this->blk_pri = (u64)RSA_size(this->rsa_pri);
    this->max_pri = this->blk_pri - RSA_PKCS1_PADDING_SIZE;
    return true;
  }
  else {
    this->blk_pri = 0;
    this->max_pri = 0;
    return false;
  }
}



bool RSA::setup_public (const c8 *filename) noexcept {
  if (this->rsa_pub)
    RSA_free(this->rsa_pub);
  BIO *bio = BIO_new_file(filename, "r");
  this->rsa_pub = PEM_read_bio_RSA_PUBKEY(bio, nullptr, nullptr, nullptr);
  BIO_free(bio);
  if (this->rsa_pub) {
    this->blk_pub = (u64)RSA_size(this->rsa_pub);
    this->max_pub = this->blk_pub - RSA_PKCS1_PADDING_SIZE;
    return true;
  }
  else {
    this->blk_pub = 0;
    this->max_pub = 0;
    return false;
  }
}



bool RSA::setup_public (const void *key, uwl size) noexcept {
  if (this->rsa_pub)
    RSA_free(this->rsa_pub);
  BIO *bio = BIO_new_mem_buf((void*)key, (s32)size);
  this->rsa_pub = PEM_read_bio_RSA_PUBKEY(bio, nullptr, nullptr, nullptr);
  BIO_free(bio);
  if (this->rsa_pub) {
    this->blk_pub = (u64)RSA_size(this->rsa_pub);
    this->max_pub = this->blk_pub - RSA_PKCS1_PADDING_SIZE;
    return true;
  }
  else {
    this->blk_pub = 0;
    this->max_pub = 0;
    return false;
  }
}



swl RSA::private_encrypt (void *dst, const void *src, uwl src_size) noexcept {
  if (!this->rsa_pri)
    return -1;
  s32 dst_size = 0;
  s32 res_size;
  auto dp = (u8*)dst;
  auto sp = (const u8*)src;
  while (src_size > this->max_pri) {
    res_size = RSA_private_encrypt((s32)this->max_pri, sp, dp, this->rsa_pri, RSA_PKCS1_PADDING);
    if (res_size < 0)
      return res_size;
    sp += this->max_pri;
    src_size -= this->max_pri;
    dp += res_size;
    dst_size += res_size;
  }
  res_size = RSA_private_encrypt((s32)src_size, sp, dp, this->rsa_pri, RSA_PKCS1_PADDING);
  if (res_size < 0)
    return res_size;
  return dst_size + res_size;
}



swl RSA::private_decrypt (void *dst, const void *src, uwl src_size) noexcept {
  if (!this->rsa_pri || src_size % this->blk_pri)
    return -1;
  s32 dst_size = 0;
  s32 res_size;
  auto dp = (u8*)dst;
  auto sp = (const u8*)src;
  while (src_size > 0) {
    res_size = RSA_private_decrypt((s32)this->blk_pri, sp, dp, this->rsa_pri, RSA_PKCS1_PADDING);
    if (res_size < 0)
      return res_size;
    sp += this->blk_pri;
    src_size -= this->blk_pri;
    dp += res_size;
    dst_size += res_size;
  }
  return dp - (u8*)dst;
}



swl RSA::public_encrypt (void *dst, const void *src, uwl src_size) noexcept {
  if (!this->rsa_pub)
    return -1;
  s32 dst_size = 0;
  s32 res_size;
  auto dp = (u8*)dst;
  auto sp = (const u8*)src;
  while (src_size > this->max_pub) {
    res_size = RSA_public_encrypt((s32)this->max_pub, sp, dp, this->rsa_pub, RSA_PKCS1_PADDING);
    if (res_size < 0)
      return res_size;
    sp += this->max_pub;
    src_size -= this->max_pub;
    dp += res_size;
    dst_size += res_size;
  }
  res_size = RSA_public_encrypt((s32)src_size, sp, dp, this->rsa_pub, RSA_PKCS1_PADDING);
  if (res_size < 0)
    return res_size;
  return dst_size + res_size;
}



swl RSA::public_decrypt (void *dst, const void *src, uwl src_size) noexcept {
  if (!this->rsa_pub || src_size % this->blk_pub)
    return -1;
  s32 dst_size = 0;
  s32 res_len;
  auto dp = (u8*)dst;
  auto sp = (const u8*)src;
  while (src_size > 0) {
    res_len = RSA_public_decrypt((s32)this->blk_pub, sp, dp, this->rsa_pub, RSA_PKCS1_PADDING);
    if (res_len < 0)
      return res_len;
    sp += this->blk_pub;
    src_size -= this->blk_pub;
    dp += res_len;
    dst_size += res_len;
  }
  return dp - (u8*)dst;
}


CLOSE_JLIB_NS
